package org.seamon.management.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.seamon.management.mapper.UserMapper;
import org.seamon.management.pojo.dto.ResetPwdDto;
import org.seamon.management.pojo.dto.UserInsertDto;
import org.seamon.management.pojo.dto.UserListDto;
import org.seamon.management.pojo.dto.UserUpdateDto;
import org.seamon.management.pojo.entity.Dept;
import org.seamon.management.pojo.entity.SecurityUser;
import org.seamon.management.pojo.entity.User;
import org.seamon.management.pojo.vo.UserDetailVo;
import org.seamon.management.pojo.vo.UserListVo;
import org.seamon.management.service.*;
import org.seamon.management.util.CacheConstants;
import org.seamon.management.util.enums.DelFlagEnum;
import org.seamon.management.util.enums.ErrorCodeEnum;
import org.seamon.management.util.enums.GenderEnum;
import org.seamon.management.util.exception.BusinessException;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Simon
 * @description 针对表【sys_user(用户/员工表)】的数据库操作Service实现
 * @createDate 2025-07-11 00:31:54
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private DeptService deptService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UserRoleService userRoleService;
    @Resource
    private RoleMenuService roleMenuService;
    @Resource
    private MenuService menuService;

    @Override
    @Cacheable(value = CacheConstants.USER_LIST, key = "#dto.toString()")
    public Page<UserListVo> listUser(UserListDto dto) {
        log.debug("未命中缓存，从数据库加载数据");
        Page<User> pageParam = new Page<>(dto.getCurrent(), dto.getSize());

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dto.getDeptId() != null, User::getDeptId, dto.getDeptId())
                .eq(dto.getStatus() != null, User::getStatus, dto.getStatus());
        Page<User> userPage = this.page(pageParam, queryWrapper);
        // 收集userPage中所有的deptId并映射为deptName
        List<Long> deptIds = userPage.getRecords().stream()
                .map(User::getDeptId)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, String> deptNameMap = deptService.listByIds(deptIds).stream()
                .collect(Collectors.toMap(Dept::getId, Dept::getName));
        // 组装userListVo
        List<UserListVo> voList = userPage.getRecords().stream()
                .map(user -> {
                    UserListVo userListVo = new UserListVo();
                    BeanUtils.copyProperties(user, userListVo);
                    String deptName = deptNameMap.get(user.getDeptId());
                    if (deptName == null) {
                        log.error("部门不存在, {}", user.getDeptId());
                        throw new BusinessException(ErrorCodeEnum.DEPT_NOT_EXIST);
                    }
                    userListVo.setDeptName(deptName);
                    return userListVo;
                })
                .toList();

        Page<UserListVo> res = new Page<>();
        res.setCurrent(userPage.getCurrent());
        res.setSize(userPage.getSize());
        res.setTotal(userPage.getTotal());
        res.setRecords(voList);

        return res;
    }

    @Override
    @Cacheable(value = CacheConstants.USER_DETAIL, key = "#id")
    public UserDetailVo getUserById(Long id) {
        log.debug("未命中缓存，从数据库加载数据");
        UserDetailVo userDetailVo = new UserDetailVo();
        User user = this.getUserByIdOrThrow(id);
        BeanUtils.copyProperties(user, userDetailVo);
        Dept dept = deptService.getById(user.getDeptId());
        if (dept == null) {
            throw new BusinessException(ErrorCodeEnum.DEPT_NOT_EXIST);
        }
        userDetailVo.setDeptName(dept.getName());
        userDetailVo.setGenderName(GenderEnum.values()[user.getGender()].ordinal());
        return userDetailVo;
    }

    @Override
    @CacheEvict(cacheNames = {CacheConstants.USER_LIST, CacheConstants.USER_DETAIL}, allEntries = true)
    public void updateUser(UserUpdateDto dto) throws BusinessException {
        log.info("更新缓存");
        User user = getUserByIdOrThrow(dto.getId());
        BeanUtils.copyProperties(dto, user);
        this.updateById(user);
    }

    @Override
    @CacheEvict(cacheNames = {CacheConstants.USER_LIST, CacheConstants.USER_DETAIL}, allEntries = true)
    public void saveUser(UserInsertDto dto) {
        log.debug("清空缓存");
        boolean exists = this.existsByUsername(dto.getUsername());
        if (exists) {
            log.error("用户名已存在");
            throw new BusinessException(ErrorCodeEnum.USER_USERNAME_EXIST);
        }
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        String pwd = passwordEncoder.encode(user.getPassword());
        user.setPassword(pwd);
        this.save(user);
    }

    @Override
    public void resetPwd(ResetPwdDto dto) throws BusinessException {
        User user = this.getUserByIdOrThrow(dto.getId());
        if (!passwordEncoder.matches(dto.getOldPassword(), user.getPassword())) {
            log.error("旧密码错误");
            throw new BusinessException(ErrorCodeEnum.USER_PWD_ERROR);
        }
        user.setPassword(passwordEncoder.encode(dto.getNewPassword()));
        this.updateById(user);
    }

    @Override
    public boolean existsByUsername(String username) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        return this.exists(queryWrapper);
    }

    @Override
    public User getByUsername(String username) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        return this.getOne(queryWrapper);
    }

    @Override
    public void updateLoginTime(String username) {
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUsername, username)
                .set(User::getLastLoginTime, LocalDateTime.now());
        this.update(updateWrapper);
    }

    @Override
    @CacheEvict(cacheNames = {CacheConstants.USER_LIST, CacheConstants.USER_DETAIL}, key = "#id")
    public void removeUserById(Long id) {
        log.debug("清空缓存");
        User user = getUserByIdOrThrow(id);
        this.removeById(user);
    }

    private User getUserByIdOrThrow(Long id) throws BusinessException {
        if (id == null) {
            throw new BusinessException(ErrorCodeEnum.USER_ID_REQUIRED);
        }
        User user = this.getById(id);
        if (user == null || user.getDelFlag().equals(DelFlagEnum.DELETED.getCode())) {
            throw new BusinessException(ErrorCodeEnum.USER_NOT_EXIST);
        }
        return user;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = this.getByUsername(username);
        if (user == null) {
            throw new BusinessException(ErrorCodeEnum.USER_NOT_EXIST);
        }

        List<Long> roleIds = userRoleService.getRole(user.getId());
        List<Long> menuIds = roleMenuService.getMenuByRoleIds(roleIds);
        List<String> permissions = menuService.getPermsByIds(menuIds);
        Collection<? extends GrantedAuthority> authorities = permissions.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        return new SecurityUser(user, authorities);
    }
}
